package tools

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"path"
	"strings"
	"time"

	"github.com/pkg/sftp"
	log "github.com/sirupsen/logrus"
	"golang.org/x/crypto/ssh"
)

type SSH客户端 struct {
	config      *ssh.ClientConfig
	session     *ssh.Session
	client      *ssh.Client
	errOut      io.Reader
	out         io.Reader
	in          io.WriteCloser
	sftp_client *sftp.Client
	AllOut      *bytes.Buffer
	schan       chan []byte
	Ip          string
}

func (s *SSH客户端) F_SSH客户端_关闭连接() {
	if s.client != nil {
		err := s.client.Close()
		if err != nil {
			log.Error("关闭ssh客户端失败")
		}
	}
	// s.schan <- []byte("结束进程")
}
func (s *SSH客户端) F_SSH客户端_连接SSH_通过公钥(username, host string, port int) bool {
	key, err := os.ReadFile("id_rsa")
	if err != nil {
		log.Fatalf("读取私钥文件失败: %v", err)
	}
	// 使用私钥创建一个Signer
	signer, err := ssh.ParsePrivateKey(key)
	if err != nil {
		log.Fatalf("解析私钥失败: %v", err)
	}
	config := &ssh.ClientConfig{
		User: username,
		Auth: []ssh.AuthMethod{
			ssh.PublicKeys(signer),
			// 可以根据需要添加其他的身份验证方法，比如公钥认证
		},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
		Timeout:         time.Second * 10,
	}
	client, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", host, port), config)
	if err != nil {
		log.Error("连接ssh服务器失败:", err)
		return false
	}

	s.client = client
	s.Ip = host
	s.config = config
	if s.f_SSH客户端_创建会话(client) == nil {
		return false
	}
	return true
}

func (s *SSH客户端) f_SSH客户端_创建会话(client *ssh.Client) *ssh.Session {
	session, err := client.NewSession()
	s.sftp_client, err = sftp.NewClient(s.client)
	if err != nil {
		log.Errorf("创建sftp客户端失败: %v\n", err)
		return nil
	}
	if err != nil {
		log.Error("创建ssh会话时失败:", err)
		return nil
	}

	s.session = session

	s.in, err = session.StdinPipe()
	if err != nil {
		log.Error("获取ssh输入管道失败:", err)
		return nil
	}
	buf := &bytes.Buffer{}
	s.AllOut = buf
	session.Stdout = buf
	session.Stderr = buf
	s.schan = make(chan []byte)
	err = session.Start("/bin/bash")
	if err != nil {
		log.Error("启动ssh会话失败:", err)
		return nil
	}
	go func(schan chan []byte) {
		for {
			temp := s.AllOut.Bytes()
			if len(temp) != 0 {
				schan <- temp
				s.AllOut.Reset()
			}
			time.Sleep(time.Millisecond * 100)
		}

	}(s.schan)
	return session
}

func (s *SSH客户端) F_客户端_上传文件(localFilepath, remoteDir string) error {
	log.Infof("开始上传文件:%s", localFilepath)
	sftpClient := s.sftp_client
	srcFile, err := os.Open(localFilepath)
	if err != nil {
		log.Errorf("打开文件[%v] 失败: %v\n", localFilepath, err)
		return err
	}
	defer srcFile.Close()
	sftpClient.MkdirAll(remoteDir)
	filename := path.Base(strings.ReplaceAll(localFilepath, `\`, `/`))
	remoteFilepath := path.Join(remoteDir, filename)
	dstFile, err := sftpClient.Create(remoteFilepath)
	if err != nil {
		log.Errorf("sftp 创建文件[%v] 失败: %v\n", remoteFilepath, err)
		return err
	}
	defer dstFile.Close()

	_, err = io.Copy(dstFile, srcFile)
	if err != nil {
		log.Errorf("sftp 拷贝文件[%v] 到目录[%v] 失败: %v\n", localFilepath, remoteDir, err)
		return err
	}
	return nil

}

func (s *SSH客户端) F_SSH客户端_连接SSH(username, password, host string, port int) bool {
	config := &ssh.ClientConfig{
		User: username,
		Auth: []ssh.AuthMethod{
			ssh.Password(password),
			// 可以根据需要添加其他的身份验证方法，比如公钥认证
		},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
		Timeout:         time.Second * 10,
	}

	client, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", host, port), config)
	if err != nil {
		log.Error("连接ssh服务器失败:", err)
		return false
	}
	s.client = client
	s.Ip = host
	s.config = config
	if s.f_SSH客户端_创建会话(client) == nil {
		return false
	}
	return true
}
func (s *SSH客户端) F_SSH客户端_判断文件是否存在(文件名称 string) bool {
	result := s.F_SSH客户端_发送指令_不同会话(fmt.Sprintf("stat %s", 文件名称))
	if strings.Contains(string(result), "没有那个文件或目录") {
		return false
	}
	return true
}

func (s *SSH客户端) F_SSH客户端_替换文件内容(文件名称, 匹配的文本, 替换的文本 string) bool {

	指令 := fmt.Sprintf("sed -i '/%s/c\\%s' %s", 匹配的文本, 替换的文本, 文件名称)
	result := s.F_SSH客户端_发送指令_不同会话(指令)
	log.Infof("替换结果:%s\n", result)
	return true
}

func (s *SSH客户端) F_SSH客户端_发送指令_不同会话(shell string) []byte {
	session, err := s.client.NewSession()
	if err != nil {
		log.Error("创建会话失败!")
		return []byte{}
	}
	session.Stdout = os.Stdout
	session.Stderr = os.Stderr
	log.Infof("执行指令:%s\n", shell)
	err = session.Run(shell)
	if err != nil {
		log.Errorf("%s执行指令失败!%s\n", shell, err)
	}
	return []byte{}
}

func (s *SSH客户端) F_SSH客户端_发送指令(shell string) []byte {
	if s.session == nil {
		log.Error("请先连接ssh客户端后在尝试执行指令!")
		return []byte{}
	}

	_, err := io.WriteString(s.in, shell+"\n")
	if err != nil {
		log.Error("向管道写出数据失败:", err)
		return []byte{}
	}
	for {
		select {
		case c := <-s.schan:
			temp := string(c)
			log.Info(temp)
			if temp == "结束进程" {
				return c
			}
		case <-time.After(time.Second * 60):
			break
		}

	}

	// if err != nil {
	// 	log.Error("读取服务器响应SSH数据失败:", err)
	// 	return []byte{}
	// }
	return []byte{}
}
func (s *SSH客户端) F_SSH客户端_判断yum是否有安装某程序(程序名称 string) bool {

	result := s.F_SSH客户端_发送指令_不同会话("yum list installed |grep " + 程序名称)

	return strings.Contains(string(result), 程序名称)
}

func (s *SSH客户端) F_SSH客户端_创建目录(目录路径 string) []byte {
	result := s.F_SSH客户端_发送指令("mkdir -p " + 目录路径)
	return result
}
func (s *SSH客户端) F_SSH客户端_创建指定大小的文件(目录路径, 文件名 string, 文件大小_字节 int) []byte {
	s.F_SSH客户端_创建目录(目录路径)
	// dd if=/dev/zero of=output_file bs=1M count=20480
	指令 := fmt.Sprintf("dd if=/dev/zero of=%s bs=1K count=%d", 目录路径+文件名, 文件大小_字节)
	// 指令:= fmt.Sprintf("fallocate -l %d %s",文件大小_字节,目录路径+文件名)
	return s.F_SSH客户端_发送指令(指令)
}

func (s *SSH客户端) F_SSH客户端_查看内核内存() []byte {
	// cat /proc/meminfo|grep SUnreclaim
	指令 := fmt.Sprintf("cat /proc/meminfo|grep SUnreclaim")
	// 指令:= fmt.Sprintf("fallocate -l %d %s",文件大小_字节,目录路径+文件名)
	return s.F_SSH客户端_发送指令(指令)
}

// func (s *SSH客户端) F_SSH客户端_发送指令_防篡改_防勒索(cmd string) ([]byte, error) {

// 	result, err := s.session.Output(cmd)
// 	if err != nil {
// 		log.Errorf("主机%s执行指令:%s,失败:%s\n", s.Ip, cmd, err)

// 		return result, err
// 	}
// 	s.session.Close()
// 	session, err := s.client.NewSession()
// 	if err != nil {
// 		log.Error("创建ssh会话时失败:", err)

// 		return []byte{}, err
// 	}
// 	s.session = session
// 	return result, err
// }
